package com.beidasoft.flip.webframe.core.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import javax.persistence.Id;

import org.apache.commons.collections.map.MultiKeyMap;
import org.apache.commons.lang.StringEscapeUtils;

import com.beidasoft.flip.webframe.core.consts.CacheConsts;
import com.beidasoft.flip.webframe.core.consts.NumTypeEnum;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CommonUtil {

    /**
     * 字符串过滤器
     * 
     * @param str
     * @return
     */
    public static String StringFilter(String value) {
        String tmp = StringEscapeUtils.escapeSql(String.valueOf(value));// sql过滤
        tmp = StringEscapeUtils.escapeJavaScript(tmp);// js脚本过滤
        tmp = StringEscapeUtils.unescapeJavaScript(tmp);// 反转义js脚本过滤
        tmp = StringEscapeUtils.escapeHtml(tmp);// html过滤
        tmp = StringEscapeUtils.unescapeHtml(tmp);// 反转义html过滤
        tmp = StringEscapeUtils.escapeJava(tmp);// java代码过滤
        tmp = StringEscapeUtils.unescapeJava(tmp);// 反转义java过滤
        return tmp;
    }

    /**
     * 通过反射获取到对象中的私有域
     * 
     * @param field 域
     * @param obj 对象实例
     * @return
     */
    public static Object getPrivateField(Field field, Object obj) {
        field.setAccessible(true);
        Object result = null;
        try {
            result = field.get(obj);
        } catch (IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
        field.setAccessible(false);
        return result;
    }

    /**
     * 通过反射生成一个新的对象
     * @param <T>
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<T> clazz, Object... constructFields) {
        try {
            if (constructFields.length == 0) {
                return clazz.newInstance();
            } else {
                List<Object> list = Arrays.asList(constructFields);
                return newInstance(clazz, list);
            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过反射生成一个含参的新的对象
     * @param <T>
     * @param clazz
     * @return
     */
    @SuppressWarnings({"rawtypes"})
    private static <T> T newInstance(Class<T> clazz, List<Object> list) {
        try {
            //            Class[] classes = new Class[list.size()];//获取所有的class
            List<Class> clazzList = list.stream().map(obj -> {
                return obj.getClass();
            }).collect(Collectors.toList());
            Class[] clazzArray = clazzList.toArray(new Class[1]);
            Constructor<T> constructor = clazz.getDeclaredConstructor(clazzArray);
            Object[] values = list.toArray(new Object[1]);
            return constructor.newInstance(values);
        } catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException
            | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过反射获取到对象中的私有域
     * @param fieldName
     * @param obj
     * @return
     */
    public static Object getPrivateField(String fieldName, Object obj) {
        return getField(fieldName, obj).map(field -> {
            return getPrivateField(field, obj);
        }).orElse(null);
    }

    /**
     * 通过反射获取域
     * @param fieldName
     * @param obj
     * @return
     */
    public static Optional<Field> getField(String fieldName, Object obj) {
        try {
            return Optional.of(obj.getClass().getDeclaredField(fieldName));
        } catch (NoSuchFieldException | SecurityException e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }

    /**
     * <p>设置私有域的值</p>
     * @param fieldName
     * @param obj
     */
    public static void setPrivateField(String fieldName, Object value, Object obj) {
        getField(fieldName, obj).ifPresent(field -> {
            field.setAccessible(true);//开启权限
            Object tmp = typeChange(field, value);//对数据进行强制类型转换
            //如果能够进行类型转换的，才能进行赋值，否则不做任何赋值处理
            if (tmp != null) {
                try {
                    field.set(obj, tmp);//赋值
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            field.setAccessible(false);//关闭权限
        });
    }

    /**
     * <p>数据的强制类型转换</p>
     * @param field
     * @param value
     * @return
     */
    public static Object typeChange(Field field, Object value) {

        if (value == null || value.equals("")) {
            return null;
        }

        if (field.getType().isAssignableFrom(value.getClass())) {
            return value;
        }

        /**
         * <p>如果两者的值的类型完全一致的话就原封不动返回</p>
         */
        if (field.getType() == value.getClass()) {
            return field.getType().cast(value);
        }

        /**
         * <p>目标对象是String，而源对象是Number的子类的话，转化为String</p>
         */
        if (field.getType() == String.class && Number.class.isAssignableFrom(value.getClass())) {
            return String.valueOf(value);
        }
        /**
         * <p>目标对象是Number的子类的话，而源对象是String的话，调用包装类转化</p>
         */
        if (Number.class.isAssignableFrom(field.getType()) && value.getClass() == String.class) {
            switch (NumTypeEnum.of(field.getType())) {
                case INT:
                    return Integer.valueOf((String)value);
                case SHORT:
                    return Short.valueOf((String)value);
                case LONG:
                    return Long.valueOf((String)value);
                case FLOAT:
                    return Float.valueOf((String)value);
                case DOUBLE:
                    return Double.valueOf((String)value);
                case BIGDECIMAL:
                    return BigDecimal.valueOf(Double.valueOf((String)value));
                case BIGINTEGER:
                    return BigInteger.valueOf(Long.valueOf((String)value));
                default:
                    break;
            }
        }

        /**
         * <p>目标对象是Date的子类的话，而源对象是String的话，调用包装类转化</p>
         */
        if (field.getType() == Date.class && value.getClass() == String.class) {
            return strToDate(String.valueOf(value), null);
        }

        /**
         * <p>目标对象是String的子类的话，而源对象是Date的话，调用包装类转化</p>
         */
        if (field.getType() == String.class && value.getClass() == Date.class) {
            return dateToStr((Date)value, null);
        }

        /**
         * <p>目标对象是Date的子类的话，而源对象是long的话，调用包装类转化</p>
         */
        if (field.getType() == String.class && value.getClass() == Long.class) {
            return new Date((long)value);
        }
        return null;
    }

    /**
     * 对象之间进行赋值
     * @param <T>
     * @param mapping
     * @param sourceList
     * @param target
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T copyPropeyties(List<Object> sourceList, T target) {
        MultiKeyMap map = CacheConsts.fieldsMapping.getFieldsMappingMap();//字段映射mapping对象
        String targetClassName = retriveClassName(target.getClass().getName());//目标对象的类名
        sourceList.stream()//开启流
            .filter(source -> {
                return map.containsKey(targetClassName, retriveClassName(source.getClass().getName()));
            })//过滤出有映射关系的
            .forEach(source -> {
                Map<String, String> fieldsMapping =
                    (Map<String, String>)map.get(targetClassName, retriveClassName(source.getClass().getName()));//获取映射对应的值
                fieldsMapping.forEach((targetField, sourceField) -> {
                    if (!targetField.contains(".") && sourceField.contains(".")) {
                        int dotIndex = sourceField.indexOf(".");
                        String collectionName = sourceField.substring(0, dotIndex);//选取集合名词
                        String fieldName = sourceField.substring(dotIndex + 1);//字段名称
                        Class<?> srcType = getField(collectionName, source).get().getType();
                        Class<?> targetType = getField(targetField, target).get().getType();
                        Object collection = getPrivateField(collectionName, source);//获取源对象中对应字段的值
                        if (collection != null) {
                            Object targetTmpField = getPrivateField(targetField, target);
                            if (srcType.isAssignableFrom(Collection.class)
                                && targetType.isAssignableFrom(Collection.class)) {
                                //如果是list的话
                                Collection<Object> srcCollection = (Collection<Object>)collection;//强制转化为list
                                List<Object> finalList = new ArrayList<>();
                                Set<Object> finalSet = new HashSet<>();
                                srcCollection.stream().forEach(src -> {
                                    Object tmpValue = getPrivateField(fieldName, src);//获取值
                                    finalList.add(tmpValue);//添加值
                                    finalSet.add(tmpValue);//添加值
                                });
                                if (targetTmpField == null && targetType == List.class) {
                                    setPrivateField(targetField, finalList, target);//将值设置到目标对象的域中
                                } else if (targetTmpField == null && targetType == Set.class) {
                                    setPrivateField(targetField, finalSet, target);//将值设置到目标对象的域中
                                } else {
                                    log.error(target.getClass().getName() + "中名称为 " + targetType.getName()
                                        + " 的字段的类型既不为List，又不为Set，不符合约定要求！请更改！");
                                }

                            } else {
                                Object tmpValue = getPrivateField(fieldName, collection);//获取值
                                setPrivateField(targetField, tmpValue, target);//将值设置到目标对象的域中
                            }
                        }
                    } else if (!targetField.contains(".")) {
                        Object sourceFieldValue = getPrivateField(sourceField, source);//获取源对象中对应字段的值
                        setPrivateField(targetField, sourceFieldValue, target);//将值设置到目标对象的域中
                    }
                });//遍历map进行赋值操作
            });//遍历对象并进行赋值操作
        Map<String, String> defaultMap = (Map<String, String>)map.get(targetClassName, "default");//默认值的映射map
        if (defaultMap != null) {
            defaultMap.forEach((k, v) -> {
                if (v.equalsIgnoreCase("uuid")) {
                    if (getPrivateField(k, target) == null) {
                        setPrivateField(k, newUuid(), target);//把uuid设置进去
                    }
                } else if (v.equalsIgnoreCase("date")) {
                    if (getPrivateField(k, target) == null) {
                        setPrivateField(k, new Date(), target);//如果是日期类型的话，把日期类型赋值
                    }
                } else {
                    if (getPrivateField(k, target) == null) {
                        setPrivateField(k, v, target);//把其他的默认值设置进去
                    }
                }
            });//对默认值的处理
        }
        return target;//返回对象本身
    }

    /**
     * <p>对象之间进行赋值</p>
     * @param <T>
     * @param mapping
     * @param source
     * @param target
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T, S> T copyPropeyties(S source, T target) {
        MultiKeyMap map = CacheConsts.fieldsMapping.getFieldsMappingMap();//字段映射mapping对象
        String targetClassName = retriveClassName(target.getClass().getName());//目标对象的类名
        //如果字段映射里面包含了映射的话
        if (map.containsKey(targetClassName, retriveClassName(source.getClass().getName()))) {
            Map<String, String> fieldsMapping =
                (Map<String, String>)map.get(targetClassName, retriveClassName(source.getClass().getName()));//获取映射对应的值
            fieldsMapping.forEach((targetField, sourceField) -> {
                if (!targetField.contains(".") && sourceField.contains(".")) {
                    int dotIndex = sourceField.indexOf(".");
                    String collectionName = sourceField.substring(0, dotIndex);//选取集合名词
                    String fieldName = sourceField.substring(dotIndex + 1);//字段名称
                    Class<?> srcType = getField(collectionName, source).get().getType();
                    Class<?> targetType = getField(targetField, target).get().getType();
                    Object collection = getPrivateField(collectionName, source);//获取源对象中对应字段的值
                    if (collection != null) {
                        Object targetTmpField = getPrivateField(targetField, target);
                        if (Collection.class.isAssignableFrom(srcType)
                            && Collection.class.isAssignableFrom(targetType)) {
                            //如果是list的话
                            Collection<Object> srcCollection = (Collection<Object>)collection;//强制转化为list
                            List<Object> finalList = new ArrayList<>();
                            Set<Object> finalSet = new HashSet<>();
                            srcCollection.stream().forEach(src -> {
                                Object tmpValue = getPrivateField(fieldName, src);//获取值
                                finalList.add(tmpValue);//添加值
                                finalSet.add(tmpValue);//添加值
                            });
                            if (targetTmpField == null && targetType == List.class) {
                                setPrivateField(targetField, finalList, target);//将值设置到目标对象的域中
                            } else if (targetTmpField == null && targetType == Set.class) {
                                setPrivateField(targetField, finalSet, target);//将值设置到目标对象的域中
                            } else {
                                log.error(target.getClass().getName() + "中名称为 " + targetType.getName()
                                    + " 的字段的类型既不为List，又不为Set，不符合约定要求！请更改！");
                            }

                        } else if (!Collection.class.isAssignableFrom(srcType)
                            && !Collection.class.isAssignableFrom(targetType)) {
                            Object tmpValue = getPrivateField(fieldName, collection);//获取值
                            setPrivateField(targetField, tmpValue, target);//将值设置到目标对象的域中
                        }
                    }
                } else if (!targetField.contains(".")) {
                    Object sourceFieldValue = getPrivateField(sourceField, source);//获取源对象中对应字段的值
                    setPrivateField(targetField, sourceFieldValue, target);//将值设置到目标对象的域中
                }
            });//遍历map进行赋值操作
        }
        Map<String, String> defaultMap = (Map<String, String>)map.get(targetClassName, "default");//默认值的映射map
        if (defaultMap != null) {
            defaultMap.forEach((k, v) -> {
                if (v.equalsIgnoreCase("uuid")) {
                    if (getPrivateField(k, target) == null) {
                        setPrivateField(k, newUuid(), target);//把uuid设置进去
                    }
                } else if (v.equalsIgnoreCase("date")) {
                    if (getPrivateField(k, target) == null) {
                        setPrivateField(k, new Date(), target);//如果是日期类型的话，把日期类型赋值
                    }
                } else {
                    if (getPrivateField(k, target) == null) {
                        setPrivateField(k, v, target);//把其他的默认值设置进去
                    }
                }
            });//对默认值的处理
        }
        return target;
    }

    /**
     * 生成一个新的uuid
     * @return
     */
    public static String newUuid() {
        return UUID.randomUUID().toString();
    }

    /**
     * <p>获取对象的主键的值</p>
     * @param <ID>
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T, ID> ID getId(T t) {
        Class<T> clazz = (Class<T>)t.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        return Optional.of(declaredFields).map(Arrays::asList).get().stream().filter(field -> {
            return field.getAnnotation(Id.class) != null;
        }).map(field -> {
            return (ID)getPrivateField(field, t);
        }).collect(Collectors.toList()).get(0);
    }

    /**
     * 对类名进行处理
     * @param className
     * @return
     */
    public static String retriveClassName(String className) {
        return className.substring(className.lastIndexOf(".") + 1);
    }

    /**
     ** 将String类型转换为Date类型
     * 
     * @param dataStr
     * @param dateFormat
     * @return
     */
    public static Date strToDate(String dateStr, String pattern) {
        boolean isNumeric = Pattern.compile("[0-9]*").matcher(dateStr).matches();// 判断字符串里面是否全是数字
        if (isNumeric) {
            long dateLong = Long.parseLong(dateStr);
            return new Date(dateLong);// 如果是long类型的就表示是date直接序列化过来的
        }
        Date d = null;
        pattern =
            pattern == null || pattern.equals("") || pattern.equalsIgnoreCase("null") ? "yyyy-MM-dd HH:mm:ss" : pattern;
        try {
            d = new java.text.SimpleDateFormat(pattern).parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return d;
    }

    /**
     ** 将date类型，通过simpleDateFormat格式进行转化成String
     * 
     * @param date
     * @param simpleDateFormat
     * @return
     */
    public static String dateToStr(Date date, String pattern) {
        pattern =
            pattern == null || pattern.equals("") || pattern.equalsIgnoreCase("null") ? "yyyy-MM-dd HH:mm:ss" : pattern;
        return new SimpleDateFormat(pattern).format(date.getTime());
    }

    /**
     * <p>设置一天的时间</p>
     * @param dateStr 时间的字符串
     * @param hourOfDay 一天的小时
     * @param minute 分钟
     * @param second 秒
     * @return
     */
    public static Date setTimeOfDay(String dateStr, Integer hourOfDay, Integer minute, Integer second) {
        Calendar cal = Calendar.getInstance();// 获取一个Calendar对象
        cal.setTime(strToDate(dateStr, null));
        cal.set(Calendar.HOUR_OF_DAY, hourOfDay);// 设置0点
        cal.set(Calendar.MINUTE, minute);// 0分
        cal.set(Calendar.SECOND, second);// 0秒
        return cal.getTime();
    }
}
